package com.oxygenecore.processor.tool;

import com.oxygenecore.processor.Expression;
import com.oxygenecore.processor.LeafExpression;
import com.oxygenecore.processor.Statement;
import com.oxygenecore.util.ShiftedOutput;

import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

/**
 * @author Askar Kalykov
 *         Date: 26.09.2010
 *         Time: 4:53:40
 */
public class ParameterParserGeneratorTool {

    public static class Test<T>{
        public Test(@Alias("parseClass") String o,
                    @Alias("parseStatement") Statement s,
                    @Alias("parseLeafExpression") LeafExpression lee,
                    @Alias("parseTypedLeafExpression") LeafExpression<Boolean> leeb,
                    @Alias("parseGenericLeafExpression") LeafExpression<T> leet,
                    @Alias("parseExpression") Expression e,
                    @Alias("parseTypedExpression") Expression<Boolean> eb,
                    @Alias("parseGenericExpression") Expression<T> et,
                    @Alias("parseStatementList") List<Statement> ls,
                    @Alias("parseLeafExpressionList") List<LeafExpression> llee,
                    @Alias("parseTypedLeafExpressionList") List<LeafExpression<Boolean>> lleeb,
                    @Alias("parseGenericLeafExpressionList") List<LeafExpression<T>> lleet,
                    @Alias("parseExpressionList") List<Expression> le,
                    @Alias("parseTypedExpressionList") List<Expression<Boolean>>leb,
                    @Alias("parseGenericExpressionList") List<Expression<T>> let
        ){}

    }


//    @ConstructorProperties("","")

    public static void main(String[] args) {
        Constructor<?> constructor = Test.class.getConstructors()[0];
        generateMethodsFromConstructor(System.out, constructor);
    }

    public static void generateMethodsFromConstructor(PrintStream output, Constructor constructor){
        out = new ShiftedOutput("    ", output);
        out.println("//generated from: " + constructor.toGenericString());
        Type[] types = constructor.getGenericParameterTypes();
        String paramName = "parameter";
        stack.push(paramName);
        for (int i = 0; i < types.length; i++) {
            Type type = types[i];
            Annotation[] annotations = constructor.getParameterAnnotations()[i];
            String methodName = "parse" + nextInt();
            if (annotations .length != 0){
                for (int j = 0; j < annotations.length; j++) {
                    Annotation annotation = annotations[j];
                    if (annotation instanceof Alias){
                        methodName = ((Alias)annotation).value();
                    }
                }
            }
            out.println("// " + type);
            out.println("public void " + methodName + "(Type " + paramName + "){");
            out.shift();
            out.println("try {");
            out.shift();
            printType(type);
            out.unshift();
            out.println("} catch (IllegalArgumentException e) {");
            out.shift();
            out.println("throw new IllegalArgumentException(" + paramName + " + " + toJavaString(" is not " + type) + ", e);");
            out.unshift();
            out.println("}");
            out.unshift();
            out.println("}");
        }
    }

    private static int nx;

    private static int nextInt(){
        return nx++;
    }

    private static Deque<String> stack = new LinkedList<String>();

    private static ShiftedOutput out;

    private static void printClass(Class clazz){
        out.println("if (" + clazz.getSimpleName() + ".class.isAssignableFrom((Class)" + stack.peek() + ")){");
        out.shift();
        String name = "class_" + nextInt();
        out.println("Class " + name + " = (Class)" + stack.peek() + ";");
        out.println("//todo add code here");
        out.unshift();
        out.println("} else {");
        printIllegalArgumentException(stack.peek() + " + " + toJavaString(" is not " + clazz.getSimpleName()));
        out.println("}");
    }

    private static void printTypeVariable(TypeVariable v){
        String name = "typeVariable_" + nextInt();
        out.println("TypeVariable " + name + " = (TypeVariable)" + stack.peek() + ";");
        String fieldName;
        fieldName = "bounds_" + nextInt();
        out.println("Type[] " + fieldName + " = " + name + ".getBounds();");
        stack.push(fieldName);
        printTypeArray(v.getBounds());
        stack.pop();
/*
        fieldName = "genericDeclaration_" + nextInt();
        out.println("GenericDeclaration " + fieldName + " = " + name + ".getGenericDeclaration();");
        stack.push(fieldName);
        printGenericDeclaration(v.getGenericDeclaration());
        stack.pop();
*/
        out.println("//todo add code here");
    }

    private static void printGenericDeclaration(GenericDeclaration genericDeclaration){
        String name = stack.peek();
        if (genericDeclaration instanceof Class<?>){
            out.println("if (" + name + " instanceof Class){");
            printClass((Class) genericDeclaration);
            out.println("} else { ");
            printIllegalArgumentException(name + " + " + toJavaString(" is not instanceof Class"));
            out.println("}");
        } else if (genericDeclaration instanceof Constructor<?>){
            out.println("if (" + name + " instanceof Constructor){");
            printConstructor((Constructor) genericDeclaration);
            out.println("} else { ");
            printIllegalArgumentException(name + " + " + toJavaString(" is not instanceof Constructor"));
            out.println("}");
        } else {
            out.println("UNKNOWN GENERIC DECLARATION (probably a method?)");
        }
    }

    private static void printConstructor(Constructor constructor) {
        out.print("<Constructor> " + constructor.getName());//not reachable?
    }

    private static String toJavaString(String message){
        return "\"" + message.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
    }

    private static void printIllegalArgumentException(String code){
        out.shift();
        out.println("throw new IllegalArgumentException(" + code + ");");
        out.unshift();
    }


    private static void printTypeArray(Type[] types){
        String arrayName = stack.peek();
        out.println("if (" + arrayName + ".length != " + types.length + ") {");
        printIllegalArgumentException(arrayName + " + " + toJavaString(" length must be " + types.length));
        out.println("}");
        for (int i = 0; i < types.length; i++) {
            Type type = types[i];
            String name = "type_" + nextInt();
            out.println("Type " + name + " = " + arrayName + "[" + i + "];");
            stack.push(name);
            printType(type);
            stack.pop();
        }
    }

    private static void printParameterizedType(ParameterizedType type){
        String name = "parameterizedType_" + nextInt();
        out.println("ParameterizedType " + name + " = (ParameterizedType)" + stack.peek() + ";");
        String fieldName;
        fieldName = "type_" + nextInt();
        out.println("Type " + fieldName + " = " + name + ".getRawType();");
        stack.push(fieldName);
        printType(type.getRawType());
        stack.pop();
        fieldName = "actualTypeArguments_" + nextInt();
        out.println("Type[] " + fieldName + " = " + name + ".getActualTypeArguments();");
        stack.push(fieldName);
        printTypeArray(type.getActualTypeArguments());
        stack.pop();
        out.println("//todo add code here");
    }

    private static void printType(Type type){
        if (type instanceof Class){
            out.println("if (" + stack.peek() + " instanceof Class){");
            out.shift();
            printClass((Class) type);
            out.unshift();
            out.println("} else {");
            printIllegalArgumentException(stack.peek() + " + " + toJavaString(" is not instanceof Class"));
            out.println("}");
        } else if (type instanceof ParameterizedType){
            out.println("if (" + stack.peek() + " instanceof ParameterizedType){");
            out.shift();
            printParameterizedType((ParameterizedType)type);
            out.unshift();
            out.println("} else {");
            printIllegalArgumentException(stack.peek() + " + " + toJavaString(" is not instanceof ParameterizedType"));
            out.println("}");
        } else if (type instanceof TypeVariable){
            out.println("if (" + stack.peek() + " instanceof TypeVariable){");
            out.shift();
            printTypeVariable((TypeVariable) type);
            out.unshift();
            out.println("} else {");
            printIllegalArgumentException(stack.peek() + " + " + toJavaString(" is not instanceof TypeVariable"));
            out.println("}");
        } else {
            out.println("UNKNOWN TYPE: "+ type + " of class " + type.getClass());
        }
    }

}
